%%%-------------------------------------------------------------------
%%% File    : hiber.erl
%%% Author  : alfonso <alfonso@alfonso-laptop>
%%% Description : 
%%%
%%% Created : 17 Sep 2009 by alfonso <alfonso@alfonso-laptop>
%%%-------------------------------------------------------------------
-module(hiberl).

-export([render/4, render_html/1]).

-record(table, {
	  name,
	  module,
	  columns = [],
	  fk_columns = [],
	  inverse_fks = []
	 }).

-record(column, {
	  name,
	  type,
	  type_size,
	  foreign_key_column,
	  pk = false
	  }).


render(Drv, ProjectName, Url, TableNames)-> 
    {ok, Ref} = odbc:connect(Url, [{{scrollable_cursors, off}}]),
    Dirs = create_dirs(ProjectName),
    Tables = lists:map( fun(TableName)-> 
				{TableName, Drv:pk(Ref, ProjectName, TableName), Drv:fk(Ref, ProjectName, TableName)} 
			end, TableNames),
    X = (catch begin 
		   Metadatas = [fill_metadata(Table, Tables, Ref) || Table <- Tables],
		   render_hrl(ProjectName, Metadatas, Dirs),
		   render_intern(ProjectName, Metadatas, Dirs)
	       end
	  ),
    odbc:disconnect(Ref),
    X.
    
create_dirs(Project)->
    {ok, Cwd} = file:get_cwd(),
    Code = filename:join(Cwd,Project),
    Doc = filename:join(Code,"doc"),
    Src = filename:join(Code,"src"),
    Ebin = filename:join(Code,"ebin"),
    file:make_dir(Code),
    file:make_dir(Doc),
    file:make_dir(Src),
    file:make_dir(Ebin),
    code:add_path(Ebin),
    {Src, Ebin, Code}.

get_template(T)->    
    filename:join(filename:join(filename:join(filename:dirname( code:where_is_file("hiberl.app") ), ".."),"templates"), T).

render_hrl(ProjectName, Tables, {Src,_,_})->
    F = fun(Table)->
		ok = erlydtl:compile(get_template("grecord.hrl"),hiberl_template_record),    
		{ok, S2} = hiberl_template_record:render( get_render_options(ProjectName, Table) ),
		S2
	end,
    file:write_file(filename:join(Src, ProjectName ++ ".hrl"), lists:map(F, Tables)),
    io:format("created file ~s.erl ~n",[ProjectName]),
    ok.

render_intern(_, [], _)-> ok;
render_intern(Project, [Table| OtherTables], {Src,Ebin, Code})-> 

    ok = erlydtl:compile(get_template("gtable2.erl"),hiberl_template),
    {ok, S} = hiberl_template:render( get_render_options(Project, Table) ),
    SourceFile = filename:join(Src, Table#table.module ++ ".erl"),
    ok = file:write_file(filename:join(Src, SourceFile), S),

    io:format("created file ~s.erl, got ~p columns ~p ~n",[Table#table.module, length(Table#table.columns), ""]),
    compile:file(SourceFile,[{outdir,Ebin}]),

    %% Reload the module
    code:purge(list_to_atom(Table#table.module)),code:load_file(list_to_atom(Table#table.module)),

    %% generate edoc
    edoc:application(list_to_atom(Project), Code, []),

    render_intern(Project, OtherTables, {Src,Ebin, Code}).

fill_metadata({Table,PKs, FK}, Tables, Ref) ->
    {ok, Columns} = odbc:describe_table(Ref, Table),

    RColumns = [ #column{name = hiberl_utils:low_first(Name), type = erl_type(Type), type_size = erl_type_size(Type), pk = lists:member(Name, PKs)} 
		 || {Name,Type} <- Columns],

    FKColumns = [ #column{name = hiberl_utils:low_first(Name),
			  type = hiberl_utils:low_first(TableFK),			  
			  foreign_key_column = PKFK} || {Name,TableFK,PKFK} <- FK],
    
    F = fun({InvTable,_,L}, Acc) ->
		Acc ++ [ #column{name =  hiberl_utils:low_first(PK),
				 type = hiberl_utils:low_first(InvTable),			  
				 foreign_key_column = Attr}
			 || {Attr, TableRef, PK} <- L, TableRef == Table]
	end,
    
    FKColumnsInv = lists:foldl(F, [], Tables),

    #table{name = Table,
	   module = hiberl_utils:low_first(Table),
	   columns = RColumns,
	   fk_columns = FKColumns,
	   inverse_fks = FKColumnsInv
	  }.

get_render_options(Project, Table)->
    [{module,Table#table.module},
     {table,Table#table.name}, 
     {columns, lists:map(fun flat_column/1, Table#table.columns)},
     {fks, lists:map(fun flat_column/1, Table#table.fk_columns)},
     {inverse_fks, lists:map(fun flat_column/1, Table#table.inverse_fks)},
     {project, Project}
    ].


flat_column(C) ->
    [{name,hiberl_utils:low_first(C#column.name)},
     {type,hiberl_utils:low_first(C#column.type)}, 
     {type_size, hiberl_utils:low_first(C#column.type_size)}, 
     {foreign_key_column,hiberl_utils:low_first(C#column.foreign_key_column)},
     {pk, atom_to_list(C#column.pk)}].

erl_type({sql_char,X})-> "string";
erl_type(sql_real)-> "float";
erl_type(sql_integer)-> "integer";
erl_type(sql_smallint)-> "integer";
erl_type({sql_varchar,X})-> "string";
erl_type('SQL_TYPE_TIMESTAMP')-> "timestamp";
erl_type(sql_tinyint)->"integer".    

erl_type_size({sql_char,X})-> X;
erl_type_size({sql_varchar,X})-> X;
erl_type_size(_)-> "undefined".

render_html([H|_] = R)->    
    Table = element(1,H),
    erlydtl:compile(get_template("table.html"),hiberl_template_table),
    F = fun (Item)-> 
		[case D of null->"null";  _-> D end|| D <-Table:get_data(Item) ]
	end,
    {ok,S} = hiberl_template_table:render([{columns,Table:columns()}, {data, lists:map(F, R)}]),
    Filename = filename:join(".", atom_to_list(Table) ++ ".html"),
    ok = file:write_file(Filename, S),
    ok.
